package org.hq.util;

import jakarta.annotation.PostConstruct;
import org.apache.http.*;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.ConnectionConfig;
import org.apache.http.config.SocketConfig;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeader;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
import java.io.*;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;

@Component
public class MyHttpClient extends TimerTask implements HttpRequestRetryHandler {
    Logger logger = LoggerFactory.getLogger(MyHttpClient.class);
    //全局参数
    private PoolingHttpClientConnectionManager connectionManager = null;
    //设置请求参数
    private RequestConfig config;
    private CloseableHttpClient client;
    //单例模式创建

    /**
     * 初始化方法
     */
    @PostConstruct
    private void init() {

        connectionManager = new PoolingHttpClientConnectionManager();
        // http请求线程池，最大连接数
        int requestMaxNum = 50000;
        ConnectionConfig connConfig = ConnectionConfig.custom().setCharset(StandardCharsets.UTF_8).build();
        SocketConfig socketConfig = SocketConfig.custom().setSoTimeout(5000).build();
        connectionManager.setDefaultConnectionConfig(connConfig);
        connectionManager.setDefaultSocketConfig(socketConfig);
        // 连接池最大生成连接数
        connectionManager.setMaxTotal(requestMaxNum);
        // 默认设置route最大连接数
        connectionManager.setDefaultMaxPerRoute(requestMaxNum);
        //设置请求参数
        config = RequestConfig.custom().setConnectTimeout(5000) //连接超时时间
                .setConnectionRequestTimeout(500) //从线程池中获取线程超时时间
                .setSocketTimeout(5000) //设置数据超时时间
                .build();
        // 创建builder
        HttpClientBuilder builder = HttpClients.custom();
        //管理器是共享的，它的生命周期将由调用者管理，并且不会关闭
        //否则可能出现Connection pool shut down异常
        builder.setConnectionManager(connectionManager).setConnectionManagerShared(true);
        // 长连接策略
        builder.setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy());
        // 创建httpClient
        client = builder.setDefaultRequestConfig(config).setRetryHandler(this).build();

        Timer timer = new Timer();
        // 第一个参数是 TimerTask，第二个参数是延迟多少毫秒开始执行，第三个参数是定时任务的执行间隔（以毫秒为单位）
        //十分钟执行一次回收链接
         timer.schedule(this, 0, 1000 * 600); // 每秒执行一次


    }

    /**
     * 请求连接池失败重试策略
     *
     * @param exception      the exception that occurred
     * @param executionCount the number of times this method has been
     *                       unsuccessfully executed
     * @param context        the context for the request execution
     * @return
     */
    @Override
    public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
        if (executionCount > 3) {
            //重试超过3次,放弃请求
            logger.error("重试超过3次,放弃请求");
            return false;
        }
        if (exception instanceof NoHttpResponseException) {
            //服务器没有响应,可能是服务器断开了连接,应该重试
            logger.error("服务器没有响应,可能是服务器断开了连接,应该重试");
            return true;
        }
        if (exception instanceof SSLHandshakeException) {
            // SSL握手异常
            logger.error("SSL握手异常");
            return false;
        }
        if (exception instanceof InterruptedIOException) {
            //超时
            logger.error("超时");
            return false;
        }
        if (exception instanceof UnknownHostException) {
            // 服务器不可达
            logger.error("服务器不可达");
            return false;
        }

        if (exception instanceof SSLException) {
            logger.error("SSLException");
            return false;
        }

        HttpClientContext httpcontext = HttpClientContext.adapt(context);
        HttpRequest request = httpcontext.getRequest();
        //如果请求不是关闭连接的请求
        return !(request instanceof HttpEntityEnclosingRequest);
    }

    /**
     * 模拟 qq 浏览器请求头
     *
     * @return 返回请求头
     */
    public  final BasicHeader[] NewResponseHeader(Map<String, String> headers) {
        if (headers == null) {
            headers = new HashMap<>();
        }

        headers.put("Sec-Ch-Ua", "Not)A;Brand;v=24, Chromium;v=116");
        headers.put("Sec-Ch-Ua-Mobile", "?0");
        headers.put("Sec-Ch-Ua-Platform", "Windows");
        headers.put("Sec-Fetch-Dest", "document");
        headers.put("Sec-Fetch-Mode", "navigate");
        headers.put("Sec-Fetch-Site", "same-origin");
        headers.put("Sec-Fetch-User", "?1");
        headers.put("Upgrade-Insecure-Requests", "1");
        String useragent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.97 Safari/537.36 Core/1.116.438.400 QQBrowser/13.0.6071.400";

       headers.put("user-agent", useragent);

        //封装请求头
        BasicHeader[] bh = new BasicHeader[headers.size()];
        int i = 0;
        for (Map.Entry<String, String> entry : headers.entrySet()) {
            bh[i++] = new BasicHeader(entry.getKey(), entry.getValue());
        }
        return bh;
    }

    /**
     * 网络请求
     * @param path
     * @return
     */
    public final String httpGetToString(String path) throws IOException {
        /**
         * 装配投文件
         */
        HttpGet get = new HttpGet(path);
        for (BasicHeader header : this.NewResponseHeader(null)) {
            get.addHeader(header);
        }

        //发送请求
        CloseableHttpResponse res =this.client.execute(get);
        //获取响应结果
        HttpEntity ent = res.getEntity();//请求实体

        String html = EntityUtils.toString(ent, StandardCharsets.UTF_8);
        res.close();
        //释放连接
        // httpClient必须releaseConnection,
        // 但不是abort。因为releaseconnection是归还连接到到连接池,
        // 而abort是直接抛弃这个连接,而且占用连接池的数目。
        get.releaseConnection();
        return html;
    }

    /**
     * 启动定时器，定时回收过期的连接
     */
    @Override
    public void run() {
        // 关闭过期的链接
        this.connectionManager.closeExpiredConnections();
        // 选择关闭 空闲30秒的链接
        this.connectionManager.closeIdleConnections(60, TimeUnit.SECONDS);
    }

    /**
     * 获取客户端
     * @return 返回http客户端相当已浏览器
     */
    public final CloseableHttpClient client(){
        return this.client;
    }
}